package cn.ad.server.common.utils;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

/**
 * hmac-sha1 解码.
 * 用于BES win_price 解密值
 * @author yu db
 *
 */
public class BESDecryptUtil {

    private final static String[] hexDigits = { "0", "1", "2", "3", "4", "5",
            "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };

    private final static String KEY_MAC = "Hmacsha1";

    // 加密
    public static String getBase64(String str) {
        byte[] b = null;
        String s = null;
        try {
            b = str.getBytes("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        if (b != null) {
            s = Base64.encodeBase64String(b);
        }
        return s;
    }

    // 解密
    public static String getFromBase64(String s) {
        String result = null;
        if (s != null) {
            try {
                result = new String(Base64.decodeBase64(s), "utf-8");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 将一个字节转化成十六进制形式的字符串
     * @param b 字节数组
     * @return 字符串
     */
    private static String byteToHexString(byte b) {
        int ret = b;
        //System.out.println("ret = " + ret);
        if (ret < 0) {
            ret += 256;
        }
        int m = ret / 16;
        int n = ret % 16;
        return hexDigits[m] + hexDigits[n];
    }

    /**
     * 转换字节数组为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String byteArrayToHexString(byte[] bytes) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < bytes.length; i++) {
            sb.append(byteToHexString(bytes[i]));
        }
        return sb.toString();
    }

    private static int parse(char c) {
        if (c >= 'a')
            return (c - 'a' + 10) & 0x0f;
        if (c >= 'A')
            return (c - 'A' + 10) & 0x0f;
        return (c - '0') & 0x0f;
    }

    public static byte[] encryptHMAC(byte[] data, byte[] key) {
        SecretKey secretKey;
        byte[] bytes = null;
        try {
            secretKey = new SecretKeySpec(key, KEY_MAC);
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.reset();
            mac.init(secretKey);
            bytes = mac.doFinal(data);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bytes;
    }

    public static byte[] hexStringToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }

    private static  byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    private static int memcmp(byte[] byte1, byte[] byte2, int length) {
        for (int i = 0; i < length; i++) {
            if (byte1[i] > byte2[i]) {
                return 1;
            }

            if (byte1[i] < byte2[i]) {
                return -1;
            }
        }

        return 0;
    }

    public static String SHA1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest.getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static long byteArrayToLong(byte[] b) {
  	
        return   b[7] & 0xFF |
                (long)(b[6] & 0xFF) << 8 |
                (long)(b[5] & 0xFF) << 16 |
                (long)(b[4] & 0xFF) << 24 |
                (long)(b[3] & 0xFF) << 32 |
                (long)(b[2] & 0xFF) << 40 |
                (long)(b[1] & 0xFF) << 48 |
                (long)(b[0] & 0xFF) << 56;
    }
    
   
    public static void main02(String[] args) {
        byte[] key = BESDecryptUtil.hexStringToBytes("005e38cb01abc22fd5acd77001abc22fd5acfe8001abc22fd5ad06509b1bb875");
        byte[] check_key = BESDecryptUtil.hexStringToBytes("005e38cb01abc22fd5ad6be001abc22fd5ad73b001abc22fd5ad7b802e88c532");
        //byte[] key = EncodeUtil.base64ToHex("Uja0xQADFz97jEpgW5IA8g0f455XNIjPRj8IqA");
        byte[] data = Base64.decodeBase64("VBAITAADlpp7jEpgW5IA8mbtB4IFAf1mB5nFuw");
//        byte[] data = Base64.decodeBase64("Uja0xQADFz97jEpgW5IA8g0f455XNIjPRj8IqA");

        byte[] iv = Arrays.copyOf(data, 16);

        byte[] p = Arrays.copyOfRange(data, 16, 24);

        byte[] sig = Arrays.copyOfRange(data, 24, 28);

        byte[] price_pad = BESDecryptUtil.encryptHMAC(iv, key);
        
        System.out.println("price_pad: "+byteArrayToLong(price_pad));

        byte[] price = new byte[p.length];
        for (int i = 0; i < p.length; i++) {
            price[i] = (byte) (p[i] ^ price_pad[i]);
        }

        byte[] conf_price = new byte[iv.length + price.length];
        for (int i = 0; i < conf_price.length; i++) {
            if (i < price.length) {
                conf_price[i] = price[i];
            } else {
                conf_price[i] = iv[i - price.length];
            }
        }
        byte[] conf_sig = BESDecryptUtil.encryptHMAC(conf_price, check_key);
        System.out.println("price: "+ byteArrayToLong(price));
        System.out.println("memcmp: "+memcmp(conf_sig, sig, sig.length));
    }
    
    
    public static void main(String[] args) throws cn.ad.server.common.exception.DecrypterException {
    	String encryptVal= "Uja0xQADFz97jEpgW5IA8g0f455XNIjPRj8IqA";
//    	String encryptVal= "VBAITAADlpp7jEpgW5IA8mbtB4IFAf1mB5nFuw";
    	String ekey = "005e38cb01abc22fd5acd77001abc22fd5acfe8001abc22fd5ad06509b1bb875";
		String ikey = "005e38cb01abc22fd5ad6be001abc22fd5ad73b001abc22fd5ad7b802e88c532";
		System.out.println("decryptVal: "+decrypt(encryptVal, ekey, ikey));
    }
    
    
    /**
     	Liunx下程序编译方式：-L 后添加openssl 
		lib对应的地址， -l后添加生成的lib名
		g++ -g -O2 encryption_test.cpp –LXXX -lcrypto -o encryption_test
		
		运行后内容如下：./ encryption_test
		ciphertext_bytes:0d1fe39e573488cf
		price_pad:0d1fe39e573488ee595acd5c6d4ce0f445476794
		value:33
		input_message:00000000000000215236b4c50003173f7b8c4a605b9200f2
		initializatio_hash:463f08a8eb74613f5fa8123cf40ce2b08589830c
		signatre:463f08a8
		
		       BES使用web_safe_base64加密方式，在base_64的基础上将“+”，“/” 替换为 “-”，“_” 
		解密的过程示例如下，附件代码示例为 2-6步。：
		
		上面的示例是 针对一个 加密串做的 ，
		解密秘钥:    005e38cb01abc22fd5acd77001abc22fd5acfe8001abc22fd5ad06509b1bb875
		校验秘钥:     005e38cb01abc22fd5ad6be001abc22fd5ad73b001abc22fd5ad7b802e88c532
		解析后价格：33
		
		解密过程(以下参数均为十六进制表示):
		1、Base64Decode(Uja0xQADFz97jEpgW5IA8g0f455XNIjPRj8IqA)，结果为:5236b4c50003173f7b8c4a605b9200f20d1fe39e573488cf463f08a8
		2、初始化向量iv  = 5236b4c50003173f7b8c4a605b9200f2（前）
		   加密价格  p   = 0d1fe39e573488cf
		   校验部分  sig = 463f08a8
		3、price_pad = hmac(解密秘钥, iv) = 0d1fe39e573488ee595acd5c6d4ce0f445476794
		4、price = p ^ price_pad = 0d1fe39e573488cf ^ 0d1fe39e573488ee595acd5c6d4ce0f445476794 = 0000000000000021
		5、conf_sig  = hmac(校验秘钥, price || iv) = 8dc4c36eb866849e4672747991c98b9445e0cda0
		6、memcmp(conf_sig, sig, 4)结果不等于0，表示校验失败
		添加使用测试账户的price_key，check_key生成的 加密 价格串：
		Price_key:
		Check_key:
		示例1：
		VBAITAADlpp7jEpgW5IA8mbtB4IFAf1mB5nFuw    解密后： 33,1410336844（价格和时间戳）
		示例2：
		VBAJ7QACOjV7jEpgW5IA8h86vvTJMTfc8QlukQ    解密后：33,1410337261
     
     * @param encryptVal 加密的值
     * @param ekey 解密秘钥
     * @param ikey 校验秘钥
     * @return 解密的值
     * @throws DecrypterException 
     * @throws cn.ad.server.common.exception.DecrypterException
     */
	public static long decrypt(String encryptVal, String ekey, String ikey) throws cn.ad.server.common.exception.DecrypterException {
		byte[] key = BESDecryptUtil.hexStringToBytes(ekey);
		byte[] check_key = BESDecryptUtil.hexStringToBytes(ikey);
		byte[] data = Base64.decodeBase64(encryptVal);
		byte[] iv = Arrays.copyOf(data, 16);
		byte[] p = Arrays.copyOfRange(data, 16, 24);
		byte[] sig = Arrays.copyOfRange(data, 24, 28);
		byte[] price_pad = BESDecryptUtil.encryptHMAC(iv, key);
		byte[] price = new byte[p.length];
		for (int i = 0; i < p.length; i++) {
			price[i] = (byte) (p[i] ^ price_pad[i]);
		}

		byte[] conf_price = new byte[iv.length + price.length];
		for (int i = 0; i < conf_price.length; i++) {
			if (i < price.length) {
				conf_price[i] = price[i];
			} else {
				conf_price[i] = iv[i - price.length];
			}
		}
		byte[] conf_sig = BESDecryptUtil.encryptHMAC(conf_price, check_key);
		long decryptVal = byteArrayToLong(price);
		if (memcmp(conf_sig, sig, sig.length) != 0) {
			throw new cn.ad.server.common.exception.DecrypterException("Signature mismatch.");
		}
		return decryptVal;
	}
}
